﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using MathMethods;

namespace Unmanaged
{
    public delegate uint LPTHREAD_START_ROUTINE(uint lpParam);

    class Class1
    {
        public const uint INFINITE = 0xFFFFFFFF;
        public const uint CREATE_SUSPENDED = 0x00000004;
        public static double[] x, y;
        [Flags]
        public enum ThreadAccess : int
        {
            TERMINATE = (0x0001),
            SUSPEND_RESUME = (0x0002),
            GET_CONTEXT = (0x0008),
            SET_CONTEXT = (0x0010),
            SET_INFORMATION = (0x0020),
            QUERY_INFORMATION = (0x0040),
            SET_THREAD_TOKEN = (0x0080),
            IMPERSONATE = (0x0100),
            DIRECT_IMPERSONATION = (0x0200)
        }

        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        public struct STARTUPINFO
        {
            public uint cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        public struct SECURITY_ATTRIBUTES
        {
            public int length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        [DllImport("Kernel32.dll")]
        public static extern uint CreateThread(
            uint lpThreadAttributes,
            uint dwStackSize,
            LPTHREAD_START_ROUTINE lpStartAddress,
            uint lpParameter,
            uint dwCreationFlags,
            out uint lpThreadId);

        [DllImport("Kernel32.dll")]
        public static extern int CloseHandle(
            uint hObject);

        [DllImport("Kernel32.dll")]
        public static extern uint WaitForSingleObject(
            uint hHandle,
            uint dwMilliseconds);

        [DllImport("Kernel32.dll")]
        public static extern void Sleep(
            uint dwMilliseconds);

        [DllImport("Kernel32.dll")]
        public static extern void ResumeThread(
            uint dwThread);

        [DllImport("kernel32.dll")]
        public static extern bool GetThreadTimes(
            IntPtr handle,
            out long creation,
            out long exit,
            out long kernel,
            out long user);

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetCurrentThread();

        [DllImport("kernel32.dll")]
        static extern IntPtr OpenThread(
            ThreadAccess dwDesiredAccess,
            bool bInheritHandle,
            uint dwThreadId);

        [DllImport("kernel32.dll")]
        static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("kernel32.dll")]
        public static extern bool GetProcessTimes(
            IntPtr handle, 
            out long creation, 
            out long exit, 
            out long kernel, 
            out long user);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr GetCurrentProcess();

        static void Main(string[] args)
        {
            ReadData();

            long
                creation = 0,
                exit = 0,
                kernel = 0,
                user = 0;

            uint dwThread2;
            LPTHREAD_START_ROUTINE lpThread2 = new LPTHREAD_START_ROUTINE(Class1.Newthon);
            uint hThreadHandle2 = CreateThread(0, 0, lpThread2, 0, CREATE_SUSPENDED, out dwThread2);

            uint dwThread3;
            LPTHREAD_START_ROUTINE lpThread3 = new LPTHREAD_START_ROUTINE(Class1.Lagrange);
            uint hThreadHandle3 = CreateThread(0, 0, lpThread3, 0, CREATE_SUSPENDED, out dwThread3);

            uint dwThread4;
            LPTHREAD_START_ROUTINE lpThread4 = new LPTHREAD_START_ROUTINE(Class1.LeastSquaresMetod);
            uint hThreadHandle4 = CreateThread(0, 0, lpThread4, 0, CREATE_SUSPENDED, out dwThread4);

            ResumeThread(hThreadHandle2);
            WaitForSingleObject(hThreadHandle2, INFINITE);
            GetThreadTimes((IntPtr)hThreadHandle2, out creation, out exit, out kernel, out user);
            echo(Elapsed(creation, exit));
            CloseHandle(hThreadHandle2);

            ResumeThread(hThreadHandle3);
            WaitForSingleObject(hThreadHandle3, INFINITE);
            GetThreadTimes((IntPtr)hThreadHandle2, out creation, out exit, out kernel, out user);
            echo(Elapsed(creation, exit));
            CloseHandle(hThreadHandle3);

            ResumeThread(hThreadHandle4);
            WaitForSingleObject(hThreadHandle4, INFINITE);
            GetThreadTimes((IntPtr)hThreadHandle2, out creation, out exit, out kernel, out user);
            echo(Elapsed(creation, exit));
            CloseHandle(hThreadHandle4);

            echo("Press enter.");
            Console.ReadLine();
        }

        /// <summary>
        /// Get elapsed time from structure.
        /// </summary>
        /// <param name="start">Start time.</param>
        /// <param name="stop">Stop time.</param>
        /// <returns>Time elapsed.</returns>
        public static TimeSpan Elapsed(long start, long stop)
        {
            long elapsed = stop - start;
            TimeSpan result = TimeSpan.FromMilliseconds(elapsed / 10000);
            return result;
        }

        public static uint Newthon(uint lpParam)
        {
            Console.WriteLine("==========\nИнтерполяционный метод Ньютона.");
            // arguments
            double[] x_ = x;
            // values
            double[] y_ = y;
            // fill values array
            //for (int i = 0; i < x_.Length; i++)
            //    y_[i] = NumericalMethods.FunctionForNewtonPolynomial(x_[i]);
            // interpolation
            double[] xi = { 1.5, 2.5, 3.5, 4.5, 5.5 };
            double[] yi = new double[xi.Length];
            for (int i = 0; i < xi.Length; i++)
                yi[i] = NumericalMethods.NewtonPolynomial(xi[i], x_, y_);
            NumericalMethods.PrintFunctionValues(xi, yi, 3);

            //double[] yr = new double[xi.Length];
            //for (int i = 0; i < xi.Length; i++)
            //    yr[i] = NumericalMethods.FunctionForNewtonPolynomial(xi[i]);
            //NumericalMethods.PrintFunctionValues(xi, yr, 3);
            return 0;
        }

        public static uint Lagrange(uint lpParam)
        {
            Console.WriteLine("==========\nИнтерполяционный метод Лагранжа");
            double[] xi = x;
            double[] yi = y;

            Console.WriteLine("Исходные точки");
            for (int i = 0; i < xi.Length; i++)
                Console.WriteLine("y(" + xi[i] + ") = " + yi[i]);

            Console.WriteLine("Проверка метода на исходных точках");
            for (int i = 0; i < xi.Length; i++)
            {
                double res = NumericalMethods.LagrangePolynomial(xi[i], xi, yi);
                if (res == yi[i])
                    Console.WriteLine("y(" + xi[i] + ") = " + res);
            }

            Console.WriteLine("Вычисление значений в промежуточных точках");
            for (int i = 1; i < xi.Length; i++)
            {
                double transitional = (xi[i - 1] + xi[i]) / 2;
                double res = NumericalMethods.LagrangePolynomial(transitional, xi, yi);
                Console.WriteLine("y(" + transitional + ") = " + res.ToString("F3"));
            }
            return 0;
        }

        public static uint LeastSquaresMetod(uint lpParam)
        {
            Console.WriteLine("==========\nМетод наименьших квадратов.");
            // заданные пользователем точки
            double[] x = new double[100], y = new double[100];
            // считываем данные из файла
            StreamReader r = new StreamReader(@"D:\MSVSProjects\Erofeev\LeastSquares\bin\Debug\data.txt", System.Text.Encoding.UTF8);
            string s = "";
            int i = 0;
            while ((s = r.ReadLine()) != null)
            {
                x[i] = Convert.ToDouble(s.Split(' ')[0]);
                y[i] = Convert.ToDouble(s.Split(' ')[1]);
                i++;
            }
            r.Close();
            // максимальная длина
            int n = Math.Min(x.Length, y.Length);

            double
                // вычисление формулы 1
                s1 = x.Aggregate(0.0, (current, item) => current + item),
                // вычисление формулы 2
                s2 = y.Aggregate(0.0, (current, item) => current + item),
                // вычисление формулы 3
                s3 = x.Aggregate(0.0, (current, item) => current + item * item),
                s4 = 0;
            // вычисление формулы 4
            for (i = 0; i < n; i++)
                s4 += x[i] * y[i];
            // вычисление коэффициентов прямой
            double a = (s4 - (s1 * s2) / n) / (s3 - s1 * s1 / n),
                   b = (s2 - a * s1) / n;
            // вывод данных
            echo("a = " + a);
            echo("b = " + b);

            //Console.WriteLine("Write the argument: ");
            // ввод аргумента и вычисление функции
            echo("Argument: ");
            double
                //arg = Convert.ToDouble(Console.ReadLine()),
                arg = Convert.ToDouble(new Random().Next(1, 10)),
                func = a * arg + b;
            echo(arg);
            echo(func);

            return 0;
        }

        public static void ReadData()
        {
            // считываем данные из файла
            StreamReader r = new StreamReader(@"D:\MSVSProjects\Erofeev\LeastSquares\bin\Debug\data.txt", System.Text.Encoding.UTF8);
            string s = "";
            List<double> xtemp = new List<double>();
            List<double> ytemp = new List<double>();

            while ((s = r.ReadLine()) != null)
            {
                xtemp.Add(Convert.ToDouble(s.Split(' ')[0]));
                ytemp.Add(Convert.ToDouble(s.Split(' ')[1]));
            }
            r.Close();
            x = xtemp.ToArray();
            y = ytemp.ToArray();
        }

        public static void echo(dynamic s)
        {
            Console.WriteLine(s);
        }
    }
}